#!/usr/bin/env python
#-*- coding:utf-8 -*-
import anydbm,os,sys,re
from py.db.DBI import DBI,dbc
from py.util.Time import Time
from py.util.Logger import logger
from py.util.Wrapper import looper
from py.io.Reader import reader
from py.util.Url import Url

class Info(object):
    
    def __init__(self,date):
        self.date=date
        self.info={}
        
    @property
    def fields(self):
        pass
    
    @property
    def table_fields(self):
        pass
    
    @property
    def mobiles(self):
        return None
    
    @property
    def mini_date(self):
        return Time.date_format(self.date,'')
    
    @property
    def ym(self):
        return Time.date_year_month(self.date)
    
    def scan_fields(self):
        for idx,field in enumerate(self.fields):
            print '%s: %s' % (idx,field)
            
    def scan_table_fields(self):
        for idx,field in enumerate(self.table_fields):
            print '%s: %s' % (idx,field)
    
    def _row_func(self,rows,mets):
        for row in rows:
            user_id,values=str(row[0]),map(lambda value:int(value),row[1:])
            self.info.setdefault(user_id,{})
            for idx,value in enumerate(values):
                met=mets.get(idx,0)
                self.info[user_id].setdefault(met,0)
                self.info[user_id][met]+=value
                
    def _get_table(self,sql):
        sql=sql.strip()
        regex=re.compile(r'select\s+.+?\s+from\s+(?:\([^\)]+\)(?:\s+(?:as)?\s+))?([^\s]+)',re.I)
        m=regex.search(sql)
        if m:
            table=m.group(1)
        else:
            table='test'
        return table
    
    def get(self,db,sql,mets,work=None):
        dbs=db if isinstance(db,(tuple,list)) else [db]
        sqls=sql if isinstance(sql,(tuple,list)) else [sql]
        if isinstance(mets,(tuple,list)):
            mets=dict(enumerate(mets))
        elif isinstance(mets,basestring):
            mets={0:mets}
        for db in dbs:
            logger.info("Get info from db '%s'." % db)
            dbi=DBI(**dbc[db])
            for sql in sqls:
                logger.info("Get info from sql: %s" % sql)
                rows=dbi.fetch(sql)
                if work is not None:
                    table=self._get_table(sql)
                    if not dbi.has_table(table):
                        msg='[%s]No table %s.%s' % (work,db,table)
                    else:
                        msg='[%s]No %s record in table %s.%s' % (work,self.date,db,table)
                    self._check(rows,msg)
                self._row_func(rows,mets)
            dbi.close()
        return self.info
    
    def _check(self,rows,msg):
        return
        if len(rows)>0:
            return
        alert_msg='<User Info Alert>%s' % msg
        logger.error(msg)
        Url.send(alert_msg,self.mobiles)
        sys.exit()
    
class User(object):
    
    def __init__(self,date):
        self.date=date
        self.dbm_file='users.dbm'
        self.sep=','
        self.default_account_attrs=self.sep.join(map(lambda s:'',range(4))+['1','0','0'])
        self.default_vip_attrs=self.sep.join(map(lambda s:'',range(4))+['0','0','0'])
    
    def _account_users(self,flag):
        sql="select user_id,left(min(create_time),10) from account where left(create_time,10)%s'%s' and type in (2,3) group by user_id" % (flag,self.date)
        name='new' if flag=='=' else 'old'
        info=[]
        for id in range(4):
            db='db_pay_account_new_%s' % id
            logger.info("Get %s account users from db '%s'." % (name,db))
            logger.info("Get %s account users from sql: %s" % (name,sql))
            dbi=DBI(**dbc[db])
            rows=dbi.fetch(sql)
            dbi.close()
            info.extend(rows)
        return info
    
    def _save_account_users(self,info):
        logger.info("1111111")
        dbm=anydbm.open(self.dbm_file,'c')
        logger.info("222")
        for row in info:
            try:
                user_id,create_date=str(row[0]),row[1]
                if user_id not in dbm:
                    dbm[user_id]='%s%s%s' % (create_date,self.sep,self.default_account_attrs)
            except Exception, e:
                print e
                continue
        dbm.close()
        logger.info("333")
        
    def old_account_users(self):
        info=self._account_users('<')
        self._save_account_users(info)
        
    def new_account_users(self):
        info=self._account_users('=')
        self._save_account_users(info)
        
    @property
    def fields(self):
        return ['post_city','post_cate1','post_cate2','post_phone','has_account','cash_account','gift_account']
    
    @property
    def table_fields(self):
        return [
            "post_city varchar(30) comment '发帖城市'",
            "post_cate1 varchar(30) comment '发帖一级类别'",
            "post_cate2 varchar(30) comment '发帖二级类别'",
            "post_phone varchar(30) comment '发帖手机'",
            "has_account smallint(2) comment '是否有账户 1:有 0:无'",
            "cash_account double comment '现金账户余额'",
            "gift_account double comment '优惠账户余额'",
        ]
        
    def _vips(self,flag='='):
        sql="select user_id,min(create_date) from order_info where order_state = 45 and product_type=10 and create_date%s'%s' group by user_id" % (flag,self.date)
        name='new' if flag=='=' else 'old'
        db='db_58_stat'
        logger.info("Get %s vips from db '%s'." % (name,db))
        logger.info("Get %s vips from sql: %s" % (name,sql))
        dbi=DBI(**dbc[db])
        rows=dbi.fetch(sql)
        dbi.close()
        return rows
    
    def _save_vips(self,info):
        dbm=anydbm.open(self.dbm_file,'c')
        for row in info:
            user_id,create_date=str(row[0]),row[1]
            if user_id in dbm:
                attrs=dbm[user_id].split(self.sep)
                if attrs[5]=='0':
                    attrs[5]='1'
                    dbm[user_id]=self.sep.join(attrs)
            else:
                dbm[user_id]='%s%s%s' %(create_date,self.sep,self.default_vip_attrs)
        dbm.close()
        
    def old_vips(self):
        info=self._vips('<')
        self._save_vips(info)
        
    def new_vips(self):
        info=self._vips('=')
        self._save_vips(info)
        
    def update_account(self,info):
        logger.info("Update account into dbm '%s'." % self.dbm_file)
        dbm=anydbm.open(self.dbm_file,'c')
        for user_id in info:
            if user_id in dbm:
                attrs=dbm[user_id].split(self.sep)
                attrs[-2],attrs[-1]=map(lambda attr:info[user_id].get(attr,'0'),('cash_account','gift_account'))
                dbm[user_id]=self.sep.join(str(attr) for attr in attrs)
            else:
                dbm[user_id]='%s%s%s' % (self.date,self.sep,self.default_account_attrs)
        dbm.close()
        
    def update_attr(self,info):
        logger.info("Update attr into dbm '%s'." % self.dbm_file)
        dbm=anydbm.open(self.dbm_file,'c')
        for user_id,attrs in info:
            if user_id in dbm:
                _attrs=dbm[user_id].split(self.sep)
                for idx in range(1,5):
                    if _attrs[idx]=='':
                        _attrs[idx]=attrs[idx-1]
                dbm[user_id]=self.sep.join(str(attr) for attr in _attrs)
        dbm.close()
        
    def _update_create_date(self):
        logger.info("Update user create date into dbm '%s'." % self.dbm_file)
        info=[]
        info.extend(self._vips('<='))
        info.extend(self._account_users('<='))
        dbm=anydbm.open(self.dbm_file,'c')
        for row in info:
            user_id,create_date=str(row[0]),str(row[1])
            if user_id in dbm:
                attrs=dbm[user_id].split(self.sep)
                dbm[user_id]= '%s%s%s' % (create_date,self.sep,dbm[user_id])
            else:
                dbm[user_id]='%s%s%s' % (create_date,self.sep,self.default_account_attrs)
        dbm.close()
    
    def _delete_create_date(self):
        logger.info("Delete user create date from dbm '%s'." % self.dbm_file)
        dbm=anydbm.open(self.dbm_file,'c')
        for user_id in dbm:
            attrs=dbm[user_id].split(self.sep)
            if len(attrs)>8:
                attrs.pop(1)
                dbm[user_id]=self.sep.join(attrs)
            elif len(attrs)<8:
                del dbm[user_id]
        dbm.close()
        
    def users(self):
        dbm=anydbm.open(self.dbm_file,'c')
        for user_id,attrs in dbm.iteritems():
            attrs=attrs.split(self.sep)
            if len(attrs)==8 and attrs[0]<=self.date:
                yield user_id,attrs[1:]
        dbm.close()
        
    def _get_users_from_user_info(self):
        info={}
        sql="select user_id,post_city,post_cate1,post_cate2,post_phone,has_account,cash_account,gift_account from user_info where stat_date='%s' " % self.date
        logger.info("Get users from table user_info.")
        dbi=DBI(**dbc['db_58_stat'])
        rows=dbi.fetch(sql)
        for row in rows:
            user_id,attrs=str(row[0]),map(lambda field:str(field) if field is not None else '',row[1:])
            info[user_id]=attrs
        return info
        
    def drop(self):
        if os.path.exists(self.dbm_file):
            os.remove(self.dbm_file)
        
    def delete(self,users=None):
        if users is None:
            users=self._disabled_users()
        elif isinstance(users,(tuple,list)):
            users=dict([(user,idx) for idx,user in enumerate(users)])
        elif isinstance(users,dict):
            users=users
        elif os.path.isfile(users):
            lines=open(users,'r').read().splitlines()
            users=dict([(user,idx) for idx,user in enumerate(lines)])
        else:
            users=dict([(user,idx) for idx,user in enumerate(users.split(','))])
        logger.info("Delete users from %s." % self.dbm_file)
        dbm=anydbm.open(self.dbm_file,'c')
        for user in users:
            if user in dbm:
                del dbm[user]
        dbm.close()
        
    def load(self,users=None,sep=','):
        if users is None:
            users=self._users()
        elif isinstance(users,(tuple,list)):
            users=dict([(user[0],map(lambda field:str(field) if field is not None else '',user[1:])) for user in users])
        elif isinstance(users,dict):
            users=users
        elif os.path.isfile(users):
            info={}
            for line in reader.read(users).split(sep).lines:
                user,attrs=line[0],line[1:]
                info[user]=attrs
            users=info
        else:
            raise Exception('Wrong users type.')
        logger.info("Load users into %s." % self.dbm_file)
        dbm=anydbm.open(self.dbm_file,'c')
        for user in users:
            if user not in dbm:
                dbm[user]=self.sep.join(users[user])
        dbm.close()
        
    def select(self,where=None):
        if where is None:
            for user_id,attrs in self.users():
                yield user_id,attrs
        for user_id,attrs in self.users():
            row=[user_id]+attrs
            bool=where(row)
            if bool is True:
                yield user_id,attrs
        
    def scan(self):
        for user_id,attrs in self.users():
            print self.sep.join([user_id]+attrs)
            
    def count(self):
        return len([user for user in self.users()])
    
    def update_edu_big_users(self):
        users='23487997,13752527621382,13752552489991,13752576803335,13752588449799,13752600396294,\
              477005793799,13752659833863,13789230223879,23329362,38346442,5649689382662,5870831864839'.replace(' ','').split(',')
        logger.info("Update educate big users into dbm '%s'." % self.dbm_file)
        dbm=anydbm.open(self.dbm_file,'c')
        for user_id in users:
            if user_id in dbm:
                attrs=dbm[user_id].split(self.sep)
                if attrs[2]=='':
                    attrs[2]='教育大客户'
                    dbm[user_id]=self.sep.join(str(attr) for attr in attrs)
        dbm.close()

class FlowInfo(Info):
    
    def __init__(self,date):
        super(FlowInfo,self).__init__(date)
        self.user=User(date)
        
    def account(self):
        sql="select user_id,account_money,account_type,UNIX_TIMESTAMP(pay_date)*1000 "
        sql+="from pay_flow where left(pay_date,10)<'%s' and account_type in (2,3) " % (self.date)
        info={}
        for id in range(128):
            db='db_pay_order_new_%s' % id
            logger.info("Get user account from db '%s'." % db)
            logger.info("Get user account from sql: %s" % sql)
            dbi=DBI(**dbc[db])
            rows=dbi.batch(sql,2000000)
            for row in rows:
                user_id,account,account_type,timestamp=str(row[0]),str(row[1]),int(row[2]),long(row[3])
                info.setdefault(user_id,{})
                at='cash' if account_type==2 else 'gift'
                info[user_id].setdefault('timestamp',0)
                if timestamp>info[user_id]['timestamp']:
                    info[user_id]['timestamp']=timestamp
                    key='%s_account' % at
                    info[user_id].setdefault(key,0)
                    info[user_id][key]=account
            dbi.close()
        self.user.update_account(info)
    
    @property
    def mer_dict(self):
        return (
            (1126,7021,'precise','精准'),
            (1022,'top','置顶'),
            (7202,'smart','智能'),
            (7023,'cpc','二手车CPC'),
            (1019,'house_auction','房产竞价'),
            (1028,'refresh','刷新'),
            (1038,'resume_download','简历下载'),
            (1032,'job_invite','面试邀请'),
            (1077,'job_post','职位发布'),
            (1071,'car_post','车源发布'),
            (1039,'phone','手机号码'),
            )
    
    @property
    def fields(self):
        fields=[]
        for field in ['deposit','consume']:
            for at in ['cash','gift']:
                fields.append('%s_%s' % (at,field))
        for field in map(lambda m:m[-2],self.mer_dict)+['other']:
            for at in ['cash','gift']:
                fields.append('%s_%s_consume' % (at,field))
        return fields
    
    @property
    def table_fields(self):
        fields=[]
        ats=[('cash','现金账户'),('gift','优惠账户')]
        for field,fcn in [('deposit','充值'),('consume','消费')]:
            for at,acn in ats:
                fields.append("%s_%s double comment '%s%s'" % (at,field,acn,fcn))
        for field,fcn in map(lambda m:(m[-2],m[-1]),self.mer_dict)+[('other','其他')]:
            for at,acn in ats:
                fields.append("%s_%s_consume double comment '%s%s消费'" % (at,field,acn,fcn))
        return fields
    
    def _row_func(self,rows,mets):
        for row in rows:
            user_id,account,amount,mer_id,sign,account_type,timestamp=str(row[0]),float(row[1]),float(row[2]),int(row[3] or 0),int(row[4]),int(row[5]),long(row[6])
            self.info.setdefault(user_id,{})
            at='cash' if account_type==2 else 'gift'
            self.info[user_id].setdefault('timestamp',0)
            if timestamp>self.info[user_id]['timestamp']:
                self.info[user_id]['timestamp']=timestamp
                key='%s_account' % at
                self.info[user_id].setdefault(key,0)
                self.info[user_id][key]=account
            if sign==1:
                key='%s_deposit' % at
                self.info[user_id].setdefault(key,0)
                self.info[user_id][key]+=amount
            elif sign==0:
                key='%s_consume' % at
                self.info[user_id].setdefault(key,0)
                self.info[user_id][key]+=amount
                product='other'
                for mer in self.mer_dict:
                    mer_ids,name=mer[:-2],mer[-2]
                    if mer_id in mer_ids:
                        product=name
                        break
                key='%s_%s_consume' % (at,product)
                self.info[user_id].setdefault(key,0)
                self.info[user_id][key]+=amount
        return self.info
    
    def flow(self):
        sql="select user_id,account_money,pay_money,mer_id,sign,account_type,UNIX_TIMESTAMP(pay_date)*1000 "
        sql+="from pay_flow where left(pay_date,10)='%s' and account_type in (2,3) " % (self.date)
        self.get(map(lambda id:'db_pay_order_new_%s' % id,range(128)),sql,None,work='Flow Info')
        self.user.update_account(self.info)
        return self.info
    
class FluxInfo(Info):
    
    def __init__(self,date):
        super(FluxInfo,self).__init__(date)
        
    @property
    def fields(self):
        return ['precise_pv','precise_click','smart_pv','smart_click','top_pv','top_click','cpc_pv','cpc_click']
    
    @property
    def table_fields(self):
        return [
            "precise_pv int(10) comment '精准展现'",
            "precise_click int(10) comment '精准点击'",
            "smart_pv int(10) comment '智能展现'",
            "smart_click int(10) comment '智能点击'",
            "top_pv int(10) comment '置顶展现'",
            "top_click int(10) comment '置顶点击'",
            "cpc_pv int(10) comment '二手车CPC展现'",
            "cpc_click int(10) comment '二手车CPC点击'",
        ]
    
    def precise_flux(self):
        sql=map(lambda id:"select user_id,sum(pv),sum(click) from stat_%s where online_date='%s' group by user_id " % (id,self.mini_date),range(8))
        return self.get('db_bid_stat',sql,mets=self.fields[0:2],work='Precise Flux')
    
    def smart_flux(self):
        sql="select user_id,sum(display_count) from ad_display_info%s where stat_date='%s' group by user_id " % (self.ym,self.date)
        self.get('db_or_ib',sql,mets=self.fields[2],work='Smart Show Flux')
        table='ad_click_income_stats_%s' % self.ym if int(self.mini_date)>=20131101 else 'ad_click_income_stats'
        sql="select user_id,sum(click_count) from %s  where stat_date='%s' group by user_id " % (table,self.date)
        return self.get('db_or_stat',sql,mets=self.fields[3],work='Smart Click Flux')
    
    def top_flux(self):
        sql="select user_id,sum(pv),sum(clicks) from top_info_detail_stats where stat_date='%s' group by user_id " % self.date 
        return self.get('db_58_stat',sql,mets=self.fields[4:6],work='Top Flux')
    
    def cpc_flux(self):
        table='precise_cpc_%s' % self.ym if int(self.mini_date)>=20130801 else 'precise_cpc'
        sql="select user_id,ifnull(sum(pv),0),ifnull(sum(click),0) from %s where stat_date='%s' group by user_id " % (table,self.date)
        return self.get('db_precise_ib',sql,mets=self.fields[6:8],work='Cpc Flux')
    
class SignInfo(Info):
    
    def __init__(self,date):
        super(SignInfo,self).__init__(date)
        
    @property
    def fields(self):
        return ['is_vip','is_bid','is_smart','is_smart_show','is_top','is_cpc']
    
    @property
    def table_fields(self):
        return [
            "is_vip smallint(2) comment '是否是会员 1:是 0:否'",
            "is_bid smallint(2) comment '是否参与竞价 1:是 0:否'",
            "is_smart smallint(2) comment '是否参与智能 1:是 0:否'",
            "is_smart_show smallint(2) comment '是否参与智能展现 1:是 0:否'",
            "is_top smallint(2) comment '是否参与置顶 1:是 0:否'",
            "is_cpc smallint(2) comment '是否参与CPC 1:是 0:否'",
        ]
    
    def _row_func(self,rows,mets):
        met=mets[0]
        for row in rows:
            user_id=str(row[0])
            self.info.setdefault(user_id,{})
            self.info[user_id][met]=1
            
    def is_vip(self):
        sql="select distinct user_id from order_info where order_state = 45 and left(service_begin_time,10)<='%s' and left(service_end_time,10)>='%s' and product_type=10 " % (self.date,self.date)
        return self.get('db_58_stat',sql,mets=self.fields[0],work='Is Vip')
    
    def is_bid(self):
        sql="select distinct user_id from auction_info_%s where stat_date='%s' " % (self.ym,self.date)
        return self.get('db_pre_ads',sql,mets=self.fields[1],work='Is Bid')
    
    def is_smart(self):
        sql="select distinct a.user_id from creative_info a left join campaign b on a.campaign_id=b.campaign_id left join subscribe c on a.subscribe_id=c.subscribe_id "
        sql+="where a.state in(0,5,6) and a.ad_source=2 and b.state=0 and b.ad_source=2 and c.state=0 and c.ad_source=2 "
        self.get('db_or_stat',sql,mets=self.fields[2],work='Is Smart')
        sql="select distinct user_id from ad_display_info%s where stat_date='%s' " % (self.ym,self.date)
        return self.get('db_or_ib',sql,mets=self.fields[3],work='Is Smart Show')
    
    def is_top(self):
        sql="select distinct UserID from topinfo_topinfo where left(StartTime,10)<='%s' and left(EndTime,10)>='%s' " % (self.date,self.date)
        return self.get('db_58_top',sql,mets=self.fields[4],work='Is Top')
    
    def is_cpc(self):
        table='precise_cpc_%s' % self.ym if int(self.mini_date)>=20130601 else 'precise_cpc'
        sql="select distinct user_id from %s where stat_date='%s' " % (table,self.date)
        return self.get('db_precise_ib',sql,mets=self.fields[5],work='Is Cpc')
    
class VipInfo(Info):
    
    def __init__(self,date):
        super(VipInfo,self).__init__(date)
        
    @property
    def fields(self):
        return ['vip_name','vip_city','vip_cate1','vip_cate2','vip_company','vip_trade',
                'vip_contact_person','vip_contact_tel','vip_saler','vip_saler_department',
                'vip_first_order_time','vip_last_order_time','vip_order_count']
    
    @property
    def table_fields(self):
        return [
            "vip_name varchar(50) comment '会员用户名'",
            "vip_city varchar(200) comment '会员城市'",
            "vip_cate1 varchar(30) comment '会员一级类别'",
            "vip_cate2 text comment '会员二级类别'",
            "vip_company varchar(50) comment '会员企业名称'",
            "vip_trade varchar(50) comment '会员企业行业'",
            "vip_contact_person varchar(50) comment '会员企业联系人'",
            "vip_contact_tel varchar(50) comment '会员企业联系电话'",
            "vip_saler varchar(30) comment '会员业务员'",
            "vip_saler_department varchar(50) comment '会员业务员所在部门'",
            "vip_first_order_time datetime comment '会员首次下单时间'",
            "vip_last_order_time datetime comment '会员上次下单时间'",
            "vip_order_count int(10) comment '会员下单次数'",
        ]
    
    def _row_func(self,rows,mets):
        for row in rows:
            user_id,values=str(row[0]),row[1:]
            self.info.setdefault(user_id,{})
            for idx,value in enumerate(values):
                met=mets.get(idx,0)
                value='' if value is None else value
                if idx==1:
                    if str(value).find(','):
                        value=str(value).split(',')[0]
                self.info[user_id][met]=value
                
    def vip_info(self):
        #sql="select o.user_id,o.user_name,p.city_name,p.cate_name1,p.cate_name2,o.ent_name,o.ent_trade,"
        #sql+="o.contact_person,o.contact_tel,o.sale_man_name,o.sale_man_department,min(o.create_time),max(o.create_time),count(o.order_id) "
        #sql+="from order_info o left join portal_info p on o.user_id=p.user_id where o.pay_amount>0 and o.product_type=10 "
        #sql+="group by o.user_id "
        sql="select user_id,user_name,city1,cate1,cate2,ent_name,ent_trade,contact_person,contact_tel,"
        sql+="sale_man_name,sale_man_department,min(create_time),max(create_time),count(order_id) "
        sql+="from order_info where order_state = 45 and product_type=10 "
        sql+="group by user_id "
        return self.get('db_58_stat',sql,mets=self.fields,work='Vip Info')
    
class ActionInfo(Info):
    
    def __init__(self,date):
        super(ActionInfo,self).__init__(date)
        
    @property
    def fields(self):
        return ['free_refresh_count','pay_refresh_count',
                'job_invite_count','job_invite_pkg_count','resume_download_count','resume_download_pkg_count',
                'resume_delivery_count','resume_read_count','job_confirm_count',
                'precise_channel_count','precise_success_channel_count','precise_success_big_channel_count',
                'post_count','modify_post_count','delete_post_count']
    
    @property
    def table_fields(self):
        return [
            "free_refresh_count int(10) comment '免费刷新次数'",
            "pay_refresh_count int(10) comment '付费刷新次数'",
            "job_invite_count int(10) comment '面试邀请次数'",
            "job_invite_pkg_count int(10) comment '面试邀请套餐次数'",
            "resume_download_count int(10) comment '简历下载次数'",
            "resume_download_pkg_count int(10) comment '简历下载套餐次数'",
            "resume_delivery_count int(10) comment '简历投递次数'",
            "resume_read_count int(10) comment '简历阅读次数'",
            "job_confirm_count int(10) comment '入职确认次数'",
            "precise_channel_count int(10) comment '精准频道数'",
            "precise_success_channel_count int(10) comment '精准竞价成功频道数'",
            "precise_success_big_channel_count int(10) comment '精准竞价成功大频道数'",
            "post_count int(10) comment '发帖数'",
            "modify_post_count int(10) comment '修改帖子数'",
            "delete_post_count int(10) comment '删除帖子数'",
        ]
    
    def refresh_action(self):
        sql="select user_id,ifnull(sum(auto_refresh_count+reserve_referesh_count),0) free_refresh_count,ifnull(sum(mannual_refresh_count),0) pay_refresh_count "
        sql+="from promotion_info_%s where stat_date='%s' group by user_id " % (self.ym,self.date)
        return self.get('db_58_stat',sql,mets=self.fields[0:2],work='Refresh Action')
    
    def job_action(self):
        sql="select user_id,sum(invite_count),sum(invite_pkg_count),sum(download_count),sum(download_pkg_count) "
        sql+="from order_pkg where stat_date='%s' group by user_id " % (self.date)
        self.get('db_zp_ib',sql,mets=self.fields[2:6],work='Job order Action')
        sql="select user_id,sum(delivery_count),sum(read_count),sum(confirm_count) from resume_cate "
        sql+="where stat_date='%s' group by user_id " % (self.date)
        return self.get('db_zp_ib',sql,mets=self.fields[6:9],work='Job Action')
    
    def precise_action(self):
        sql="select user_id,count(distinct channel_id),count(distinct(if(state=0,channel_id,null))),"
        sql+="count(distinct(if((state=0 and city2='' and cate2!='' and cate3=''),channel_id,null))) "
        sql+="from auction_info_%s where stat_date='%s' group by user_id " % (self.ym,self.date)
        return self.get('db_pre_ads',sql,mets=self.fields[9:12],work='Precise Action')
    
    def post_action(self):
        sql="select user_id,sum(post_count),sum(modify_post_count),sum(delete_post_count) from user_post where stat_date='%s' group by user_id" % (self.date)
        return self.get('db_58_ib',sql,mets=self.fields[12:15],work='Post Action')
    
class AttrInfo(object):
    def __init__(self,date):
        self.date=date
        self.info={}
        self.user=User(date)
        
    @property
    def path(self):
        return '/data/logs/wuba/user/post/file'
    
    @property
    def users(self):
        return dict([(user,0) for user,attrs in self.user.users()])
    
    def _old_attr(self):
        users=self.users
        info={}
        def _parse_attr(attrs,name):
            info[user_id].setdefault(name,{})
            for attr in attrs.split('#'):
                key,count,time=re.split(r':|,',attr)
                info[user_id][name].setdefault(key,{})
                info[user_id][name][key].setdefault('count',0)
                info[user_id][name][key]['count']+=int(count)
                info[user_id][name][key].setdefault('time',0)
                if info[user_id][name][key]['time']<float(time):
                    info[user_id][name][key]['time']=float(time)
                
        for line in reader.read(self.path+'/user_attr_20130*').split('|').lines:
            user_id,locals,cates,phones=line[1:]
            if user_id not in users:
                continue
            info.setdefault(user_id,{})
            _parse_attr(locals,'locals')
            _parse_attr(cates,'cates')
            _parse_attr(phones,'phones')
            
        return info
    
    def _old_info(self):
        def _get_attr(user_id,field):
            sorted_attrs=sorted(info[user_id].get(field,{}).iteritems(),key=lambda v:(v[1]['count'],Time.str2time(v[1]['time'])),reverse=True)
            if len(sorted_attrs)>0:
                attr,_=sorted_attrs[0]
            else:
                attr=''
            return attr
        
        info=self._old_attr()
        for user_id in info:
            attrs=map(lambda field:_get_attr(user_id,field),('locals','cates','phones'))
            city,cates,phone=attrs
            if cates=='':
                cate1,cate2='',''
            else:
                cate1,cate2=cates.split('_')
            yield user_id,(city,cate1,cate2,phone)
    
    def old_attr(self):
        info=self._old_info()
        self.user.update_attr(info)
    
    def _new_attr(self):
        for line in reader.read(self.path+'/user_post_%s' % Time.date_format(self.date,'')).split('|').lines:
            user_id,city,cate1,cate2,phone,post_count=line[1:7]
            if int(post_count)>0:
                yield user_id,(city,cate1,cate2,phone)
                
    def new_attr(self):
        info=self._new_attr()
        self.user.update_attr(info)
    
class UserInfo(Info):
    
    def __init__(self,date=Time.yesterday(),path='.'):
        super(UserInfo,self).__init__(date)
        self.date=Time.date_format(date)
        self.user=User(self.date)
        self.flow=FlowInfo(self.date)
        self.flux=FluxInfo(self.date)
        self.sign=SignInfo(self.date)
        self.vip=VipInfo(self.date)
        self.action=ActionInfo(self.date)
        self.attr=AttrInfo(self.date)
        self.sep='$'
        self.output_file='%s%suser_info_%s' % (path,os.sep,Time.date_format(self.date,''))
        
    def old_users(self,is_drop=False):
        if is_drop:
            self.user.drop()
        self.user.old_account_users()
        self.flow.account()
        self.user.old_vips()
        self.attr.old_attr()
        
    def new_users(self):
        self.user.new_account_users()
        self.user.new_vips()
        self.attr.new_attr()
        
    def users(self,is_update=False,is_drop=False):
        if is_update:
            self.old_users(is_drop)
        self.new_users()
        return self.user.users()
        
    def flow_info(self):
        self.flow.flow()
        return self.flow.info
    
    def flux_info(self):
        self.flux.precise_flux()
        self.flux.smart_flux()
        self.flux.top_flux()
        self.flux.cpc_flux()
        return self.flux.info
    
    def sign_info(self):
        self.sign.is_vip()
        self.sign.is_bid()
        self.sign.is_smart()
        self.sign.is_top()
        self.sign.is_cpc()
        return self.sign.info
    
    def vip_info(self):
        self.vip.vip_info()
        return self.vip.info
    
    def action_info(self):
        self.action.refresh_action()
        self.action.job_action()
        self.action.precise_action()
        self.action.post_action()
        return self.action.info
    
    def user_info(self,is_update=False,is_drop=False):
        def _metrics(info,user_id,fields,default=0):
            if user_id not in info:
                return map(lambda met:default,fields)
            else:
                return map(lambda key:info[user_id].get(key,default),fields)
        users=self.users(is_update,is_drop)
        flow=self.flow_info()
        flux=self.flux_info()
        sign=self.sign_info()
        vip=self.vip_info()
        action=self.action_info()
        f=open(self.output_file,'w')
        for user_id,attrs in users:
            signs=_metrics(sign,user_id,self.sign.fields)
            vips=_metrics(vip,user_id,self.vip.fields,default='')
            vips[-1]=0 if vips[-1]=='' else vips[-1]
            flows=_metrics(flow,user_id,self.flow.fields)
            fluxs=_metrics(flux,user_id,self.flux.fields)
            actions=_metrics(action,user_id,self.action.fields)
            f.write(self.sep.join(str(field) for field in [self.date,user_id]+attrs+signs+vips+flows+fluxs+actions)+'\n')
        f.close()
        
    @property
    def fields(self):
        fields=['stat_date','user_id']
        for field in self.user.fields+self.sign.fields+self.vip.fields+self.flow.fields+self.flux.fields+self.action.fields:
            fields.append(field)
        return fields
        
    @property
    def table_fields(self):
        fields=["stat_date date comment '统计日期'","user_id bigint(20) comment '用户ID'"]
        for field in self.user.table_fields+self.sign.table_fields+self.vip.table_fields+self.flow.table_fields+self.flux.table_fields+self.action.table_fields:
            fields.append(field)
        return fields
    
    @property
    def table(self):
        return "user_info_%s" % self.ym
    
    def load(self):
        dbi=DBI(**dbc['db_58_ib'])
        dbi.save(self.output_file,self.table,self.table_fields,sep=self.sep,is_update=False,where={'stat_date':self.date},options={'ENGINE':'BRIGHTHOUSE'})
        dbi.close()
        
    def dump(self):
        sql="select * from %s where stat_date='%s' " % (self.table,self.date)
        dbi=DBI(**dbc['db_58_ib'])
        dbi.dump(sql,file=self.output_file,sep=self.sep)
        dbi.close()
        
@looper('DAY')
def main(date):
    try:
        path='/data/logs/wuba/user/user_info'
        ui=UserInfo(date,path)
        if not os.path.exists(ui.output_file):
            ui.user_info()
        ui.load()
    except Exception,error:
        logger.error("Running Error: %s" % str(error))
        Url.send("<User Info Alert>[Running Error]%s " % str(error))
        sys.exit()
    
if __name__ == '__main__':
    main()
    

